<template>
  <div
    class="relative"
    ref="containerTarget"
    :style="{
      height: containerHeight + 'px'
    }"
  >
    <template v-if="columnWidth && data.length">
      <div
        v-for="(item, index) in data"
        :key="nodeKey ? item[nodeKey] : index"
        class="m-waterfall-item absolute duration-300"
        :style="{
          width: columnWidth + 'px',
          left: item._style?.left + 'px',
          top: item._style?.top + 'px'
        }"
      >
        <slot :item="item" :width="columnWidth" :index="index"></slot>
      </div>
    </template>
    <div v-else>加载中。。。</div>
  </div>
</template>

<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import {
  getImgElements,
  getAllImg,
  onComplateImgs,
  getMinHeightColumn,
  getMinHeight,
  getMaxHeight
} from './utils'

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  nodeKey: {
    type: String
  },
  column: {
    default: 2,
    type: Number
  },
  columnSpacing: {
    default: 20,
    type: Number
  },
  rowSpacing: {
    default: 20,
    type: Number
  },
  picturePreReading: {
    type: Boolean,
    default: true
  }
})

const containerHeight = ref(0)
//记录每列高度的容器
const columnHeightObj = ref({})

//构建记录每列高度的对象
const useColumnHeightObj = () => {
  columnHeightObj.value = {}
  for (let i = 0; i < props.column; i++) {
    columnHeightObj.value[i] = 0
  }
}

const containerTarget = ref(null)

//容器的总宽度
const containerWidth = ref(0)

//容器的左边距
const containerLeft = ref(0)

//计算容器的宽度
const useContainerWidth = () => {
  const { paddingLeft, paddingRight } = getComputedStyle(
    containerTarget.value,
    null
  )
  containerLeft.value = parseFloat(paddingLeft)
  containerWidth.value =
    containerTarget.value.offsetWidth -
    parseFloat(paddingLeft) -
    parseFloat(paddingRight)
}

//列宽
const columnWidth = ref(0)

//所有的列间距
const columnSpacingTotal = computed(() => {
  return (props.column - 1) * props.columnSpacing
})

//计算列宽
const useColumnWidth = () => {
  useContainerWidth()
  columnWidth.value =
    (containerWidth.value - columnSpacingTotal.value) / props.column
}

onMounted(() => {
  useColumnWidth()
})

//图片高度集合
let itemHeights = []
//监听图片加载完成
const waitImgComplate = () => {
  itemHeights = []
  //获取所有类名为m-waterfall-item的元素
  let itemElements = [...document.getElementsByClassName('m-waterfall-item')]
  //获取img标签
  const imgElements = getImgElements(itemElements)
  const allImgs = getAllImg(imgElements)
  onComplateImgs(allImgs).then(() => {
    //图片加载完成
    itemElements.forEach((el) => {
      itemHeights.push(el.offsetHeight)
    })
    //渲染位置
    useItemLocation()
  })
}

//不需要监听图片加载
const useItemHeight = () => {
  itemHeights = []
  let itemElements = [...document.getElementsByClassName('m-waterfall-item')]
  itemElements.forEach((el) => {
    itemHeights.push(el.offsetHeight)
  })
  useItemLocation()
  console.log(itemHeights)
}

//触发时机
watch(
  () => props.data,
  (newValue) => {
    nextTick(() => {
      const resetColumnHeight = newValue.every((item) => !item._style)
      if (resetColumnHeight) {
        useColumnHeightObj()
      }
      if (props.picturePreReading) {
        waitImgComplate()
      } else {
        useItemHeight()
      }
    })
  },
  {
    deep: true,
    immediate: true
  }
)

//监听列数的变化,响应式变化
watch(
  () => props.column,
  () => {
    columnWidth.value = 0
    nextTick(() => {
      reset()
    })
  }
)

const reset = () => {
  setTimeout(() => {
    useColumnWidth()
    //重制_style ,触发监听器
    props.data.forEach((item) => {
      item._style = null
    })
  }, 100)
}

//处理定位
const useItemLocation = () => {
  props.data.forEach((item, index) => {
    //避免重复计算
    if (item._style) {
      return
    }
    item._style = {}
    item._style.left = getItemLeft()
    item._style.top = getItemTop()

    console.log(getItemLeft())
    console.log(getItemTop())
    //指定的列高度的自增
    increasingHeight(index)
  })
  containerHeight.value = getMaxHeight(columnHeightObj.value)
}

//组件销毁时，清除所有的_style
onUnmounted(() => {
  props.data.forEach((item) => {
    delete item._style
  })
})

//获取下一个item左宽度
const getItemLeft = () => {
  const column = getMinHeightColumn(columnHeightObj.value)
  return (
    column * (columnWidth.value + props.columnSpacing) + containerLeft.value
  )
}

//获取下一个item的高度
const getItemTop = () => {
  return getMinHeight(columnHeightObj.value)
}

const increasingHeight = (index) => {
  const minHeightColumn = getMinHeightColumn(columnHeightObj.value)
  columnHeightObj.value[minHeightColumn] +=
    itemHeights[index] + props.rowSpacing
}
</script>

<style lang="scss"></style>
